#define DEBUG         0 
#define FP_PRE        0.0000001
#define USAGE_MESSAGE "Usage: llsellipsoid <file>"
#define DATA_POINTS   4
#define EVIDENCE_TRSH 5632
#define CENTROID_TRSH_HIGH 100
#define CENTROID_TRSH_LOW 20
#define MAX_CENTROIDS 2048
#define AO_HIGH	2048  //determines how many iterations are needed to recover remaining parameters

#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <string.h>
#include <time.h>
#include <math.h>
#include <cmath>
#include <fstream>
#include <math_functions.h>
#include <cuda.h>
using std::ifstream;
using std::ofstream;
	
#include <iostream>
using std::cout;
using std::endl;
using std::cerr;

#include "util.h"
#include "centroid_solver_kernel.cu"
#include "rngarray_kernel.cu"
#include "process_accum_kernel.cu"
#include "merge_accum_kernel.cu"
#include "recover_remaining_parameters.cu"
#include "translate_edge_pxls.cu"
#include "process_remaining_parameters.cu"
#include "average_axisOri.cu"
#include "reduce_remaining_parameters.cu"

//RNG Data
extern cuda_dim RNGArray_dim;
uint            rnds_cnt = EVIDENCE_TRSH * DATA_POINTS * 100;//Solver_dim.grid_dim.x * Solver_dim.block_dim.x * 6;

void setup(int* argc, char **argv);
void read_Image(char* fileName);
void terminate();
void read_in_rnds();

int main (int argc, char *argv[]) 
{
	//ellipsoid_t ellipsoid;
	//timespec start, end, t_ge, t_pa;
	//printf("Wait a minute...\n");
	
	hr_timer_t rng_time;
	
	cudaError_t err;
	
//	read_in_rnds();

	// setup the program
	setup(&argc,argv);

	cudaSetDevice(1);

	printf("Setup DONE.\n");
	
	timer_start(&rng_time);
	timer_start(&rng_time);
	RNGArray_kernel<<<RNGArray_dim.grid_dim,RNGArray_dim.block_dim>>>(d_rnds,rnds_cnt);
	err = cudaThreadSynchronize();
	
	if (err != cudaSuccess) {
		debug("RNGArray_kernel: %s",cudaGetErrorString(err));
		exit(-1);
	}
	
	printf("RNG kernerl called\n");
	
	//printRandoms(rnds_cnt);
	
	dim3 dimBlock(128);
	dim3 dimGrid(44);
	
	printf("calling __global__ gather_evidence()....\n");
	
	gather_evidence<<<dimGrid, dimBlock>>>(
		d_edge_pxls,
		h_edge_pxl_cnt,
		d_rnds,
		d_voters,
		d_possCentroids
	);

	printf("finished calling __global__ gather_evidence()....\n");
	
	err = cudaThreadSynchronize();
	
	if(err != cudaSuccess)
		printf("after gather_evidence kernel %s\n",cudaGetErrorString(err));
	printf("calling __global__ processAccum()....\n");
	
	processAccum<<<dimGrid, dimBlock>>>(d_possCentroids);
	
	printf("finished calling __global__ processAccum()....\n");
	
	err = cudaThreadSynchronize();
	
	if(err != cudaSuccess)
		printf("after process accum kernel %s\n",cudaGetErrorString(err));
	
	mergeAccum<<<dimGrid, dimBlock>>>(d_nearest, d_finalCentroids, d_finalVoters);
	
	err = cudaThreadSynchronize();

	if(err != cudaSuccess)
		printf("after merge kernel  %s\n",cudaGetErrorString(err));

	// get how many centroids were detected so we know how many threads 
	// to launch in the next kernel, for edge pixel translation
//	cudaMemcpy(h_centroidLocation, d_centroidLocation, sizeof(int), cudaMemcpyDeviceToHost);

	translatePoints<<<32, 64>>>(d_finalVoters, d_finalCentroids);
	err = cudaThreadSynchronize();

	if(err != cudaSuccess)
		printf("after translate points kernel %s\n",cudaGetErrorString(err));

	findAxisLengthsAndOrientation<<<32, 64>>>(d_finalCentroids, d_finalVoters, d_rnds, d_axisOri);
	err = cudaThreadSynchronize();

	if(err != cudaSuccess)
		printf("after find Axis Ori kernel %s\n",cudaGetErrorString(err));

	processAxisOri<<<32, 64>>>(d_axisOri);
	err = cudaThreadSynchronize();

	if(err != cudaSuccess)
		printf("after processAxisOri kernel %s\n",cudaGetErrorString(err));

	averageAxisOri<<<32, 64>>>(d_axisOri);
	err = cudaThreadSynchronize();

	if(err != cudaSuccess)
		printf("after reduce accum kernel %s\n",cudaGetErrorString(err));

	printf("after averageAxisOri================\n");

	reduceRemainingParameters<512><<<1, 512, sizeof(int) * 512>>>(d_axisOri, AO_HIGH);
	err = cudaThreadSynchronize();

	if(err != cudaSuccess)
		printf("after reduce accum kernel %s\n",cudaGetErrorString(err));

	cudaMemcpy(h_axisOri, d_axisOri, sizeof(AxisOrientation_t) * AO_HIGH, cudaMemcpyDeviceToHost);

	printf("****************ORIENTATION AND AXIAL LENGTHS********************\n");
	for(int i = 0; i < AO_HIGH; i++)
	{
		if(h_axisOri[i].votes > 1)
		{
			cout << "index: " << i << "\t A: " << h_axisOri[i].a/10000.0 << "\t B: " << h_axisOri[i].b/10000.0 << "\t C: " << h_axisOri[i].c/10000.0;	
			cout << "\t alpha: " << h_axisOri[i].alpha/10000.0 << "\t beta: " << h_axisOri[i].beta/10000.0 << "\t theta: " << h_axisOri[i].theta/10000.0;
			cout << "\t votes: " << h_axisOri[i].votes << "\t centroid: " << h_axisOri[i].centroid << endl;
		}
//		printf("index: %d\t, A: %d\t B:%d\t C:%d\t alpha: %d\t beta: %d\t theta: %d\t votes:%d\n", i, h_axisOri[i].a, h_axisOri[i].b, h_axisOri[i].c, h_axisOri[i].alpha,  h_axisOri[i].beta,  h_axisOri[i].theta, h_axisOri[i].votes);
	}

	
//	cudaMemcpy(h_nearest, d_nearest, sizeof(centroid_list)*MAX_CENTROIDS * EVIDENCE_TRSH, cudaMemcpyDeviceToHost);
	
	timer_end(&rng_time);
	
	printf("run time: %f\n", compute_secs(&rng_time.elapsed));
	
//	err = cudaMemcpy(h_possCentroids, d_possCentroids, sizeof(centroid_list) * EVIDENCE_TRSH, cudaMemcpyDeviceToHost);

	if(err != cudaSuccess)
		printf("after memcpy possCentroid %s\n",cudaGetErrorString(err));
	
/* 	for(int k = 0; k < MAX_CENTROIDS * EVIDENCE_TRSH;k++)
	{
		if(h_nearest[k].votes >= 6)
		{
			printf("index d_nearest %d ... x: %f, y: %f, z: %f, ..... votes: %d\n", k, h_nearest[k].meanCentroid.x, h_nearest[k].meanCentroid.y, h_nearest[k].meanCentroid.z, h_nearest[k].votes);
		}
	} 
*/
	int cnt = 0;
/*	for(int j = 0; j < EVIDENCE_TRSH; j++)
	{
		if((int)h_possCentroids[j].centroid.x >= 45 && (int)h_possCentroids[j].centroid.x <= 53 &&
		   (int)h_possCentroids[j].centroid.y >= 45 && (int)h_possCentroids[j].centroid.y <= 53 &&
		   (int)h_possCentroids[j].centroid.z >= 45 && (int)h_possCentroids[j].centroid.z <= 53 &&
		   h_possCentroids[j].votes > 20)
		//h_possCentroids[j].meanCentroid.x >= 48.0 && h_possCentroids[j].meanCentroid.x <= 51.0)
	//	if(h_possCentroids[j].votes >= 6)
		{
			printf("index %d ... x: %f, y: %f, z: %f, ..... votes: %d\n", j, h_possCentroids[j].meanCentroid.x, h_possCentroids[j].meanCentroid.y, h_possCentroids[j].meanCentroid.z, h_possCentroids[j].votes);
//			printf("index %d ... x: %f, y: %f, z: %f, ..... votes: %d\n", j, h_possCentroids[j].centroid.x, h_possCentroids[j].centroid.y, h_possCentroids[j].centroid.z, h_possCentroids[j].votes);
			cnt++;
		}
	}

	printf("count: %d\n", cnt);
*/
	cudaMemcpy(h_finalCentroids, d_finalCentroids, sizeof(centroid_list) * MAX_CENTROIDS, cudaMemcpyDeviceToHost);

	timer_end(&rng_time);
	
	printf("run time: %f\n", compute_secs(&rng_time.elapsed));
/*
	for(int i = 0; i < 10; i++)
		printf("fC: %d, x: %.8f, y:%.8f, z:%.8f, votes: %d\n", i,  h_finalCentroids[i].meanCentroid.x, h_finalCentroids[i].meanCentroid.y, h_finalCentroids[i].meanCentroid.z, h_finalCentroids[i].votes);

	cudaMemcpy(h_voters, d_voters, sizeof(centroid_list) * DATA_POINTS * EVIDENCE_TRSH, cudaMemcpyDeviceToHost);
	cudaMemcpy(h_finalVoters, d_finalVoters, sizeof(centroid_list) * DATA_POINTS * EVIDENCE_TRSH, cudaMemcpyDeviceToHost);
	printf("\n\n\n");

	for(int i = 0; i < DATA_POINTS * EVIDENCE_TRSH; i++)
		printf("oV: %d, x: %.8f, y:%.8f, z:%.8f, vote: %d\n", i,  h_voters[i].point.x, h_voters[i].point.y, h_voters[i].point.z, h_voters[i].centroidIndex);

	printf("\n\n\n");

	for(int i = 0; i < 1000; i++)
		printf("fV: %d, x: %.8f, y:%.8f, z:%.8f, vote: %d\n", i,  h_finalVoters[i].point.x, h_finalVoters[i].point.y, h_finalVoters[i].point.z, h_finalVoters[i].centroidIndex);
//	printf("count : %d\n", cnt);
	
	printf("size of poss centroids: %d\n", sizeof(centroid_list)*MAX_CENTROIDS);
*/	
	//printRandoms(rnds_cnt);	

	// gather evidence (timed)
	//clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&start);
	//gather_evidence();
	
	cudaFree(d_possCentroids);
	cudaFree(d_edge_pxls);
	cudaFree(d_voters);
	cudaFree(d_nearest);
	cudaFree(d_finalCentroids);
	cudaFree(d_finalVoters);
	cudaFree(d_axisOri);

	terminate();
	return 0;
}

void read_in_rnds()
{
	
	ifstream inFile("rnds.txt");
	int rnd;
	h_rnds = new uint[rnds_cnt];

		printf("amount of random numbers: %d\n", rnds_cnt);
	for(int i = 0; i < rnds_cnt; i++)
	{
		inFile >> rnd;
		h_rnds[i] = rnd;
		//printf("random number %d: %d\n", i+1, h_rnds[i]);
	}
	

}

void setup(int* argc, char **argv) {
	
	//printf("Whoa.\n");
	int pos;

	// check the lenght of the cmd line
	if (*argc != 2)
		cerr << USAGE_MESSAGE << endl;

	read_Image(argv[1]);
	
	setup_RNGArray(rnds_cnt);

	setup_Centroid_Solver();
	// setup the accumulator
	setup_accum();

}

void read_Image(char* fileName)
{
	// load the input image
	//debug("Reading in file %s...",fileName);
	
	//cout << in_filename << endl;
	//printf("file name: %s\n", argv[1]);
	
	ifstream inFile(fileName);
	double temp;
	
	inFile >> temp >> temp >> temp >> temp >> temp >> temp >> temp >> temp >> temp >> temp >> h_edge_pxl_cnt;
	
	//cout << h_edge_pxl_cnt << endl;
	
	h_edge_pxls = new edge_pixel_t[h_edge_pxl_cnt];
	memset(h_edge_pxls,0,sizeof(edge_pixel_t) * h_edge_pxl_cnt);
	
	//printf("Before for.\n");
	
	for(int i = 0; i < h_edge_pxl_cnt; i++)
	{
		inFile >> temp;
		h_edge_pxls[i].x = temp;

		inFile >> temp;
		h_edge_pxls[i].y = temp;

		inFile >> temp;
		h_edge_pxls[i].z = temp;
	}
	//printf("After for.\n");
	
}

void terminate() {
	if (h_edge_pxls != NULL) {
		delete [] h_edge_pxls;
		h_edge_pxls    = NULL;
		h_edge_pxl_cnt = 0;
	}

	delete [] h_voters;
	h_voters = NULL;
	delete [] h_nearest;
	h_nearest = NULL;
	delete [] h_possCentroids;
	h_possCentroids = NULL;


	//shutdown_accum();
}
